{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Improve Momentum Trading strategies using Hurst\n",
    "\n",
    "In this notebook we'll implementing a strategy to trade on momentum. You'll be using the training wheels version of Auquan's toolbox to abstract out the details since the full version of toolbox is a bit comprehensive to get started with. \n",
    "\n",
    "We're providing you with a bare-bones version that shows you how to use 30 day momentum to trade on Apple, sometime between 2015 and 2017. This naive strategy loses money and that's to be expected. Your goal is to make use of Hurst exponent that you learnt in previous lessons to create a better strategy. \n",
    "\n",
    "This is an analytical method of momentum trading, but it is also to turn this into a machine learning problem. This is discussed at the end of the notebook, with a link to an extension exercise on the quant-quest website from Auquan for you to attempt this approach.\n",
    "\n",
    "**Goals**:\n",
    "1. Understand how the barebones momentum version is working and make yourself comfortable with it\n",
    "2. Use the Hurst exponent to create a money making strategy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%load_ext autoreload\n",
    "%autoreload 2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's import everything we need to run our backtesting algorithm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install qq-training-wheels auquan_toolbox --upgrade"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from qq_training_wheels.momentum_trading import MomentumTradingParams\n",
    "from backtester.trading_system import TradingSystem\n",
    "from backtester.features.feature import Feature\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The class below implements all the logic you need to run the momentum backtester. Go through it and make sure you understand each part. You can run it first and make changes later to see if you made any improvements over the naive strategy.\n",
    "\n",
    "There are 6 functions within the class:\n",
    "\n",
    "- \\_\\_init\\_\\_\n",
    "- getSymbolsToTrade\n",
    "- getInstrumentFeatureConfigDicts\n",
    "- getPredictions\n",
    "- hurst_f\n",
    "- updateCount\n",
    "\n",
    "**__init__**\n",
    "\n",
    "Initializes the class\n",
    "\n",
    "**getSymbolsToTrade**\n",
    "\n",
    "This is where we can select which stocks we want to test our strategy on. Here we're using just AAPL is it is the only ticker returned\n",
    "\n",
    "**getInstrumentConfigDicts** \n",
    "\n",
    "This is the way that the toolbox creates features that we want to use in our logic. It's really important for resource optimisation at scale but can look a little daunting at first. We've created the features you'll need for you. If you're interested in learning more you can here: https://blog.quant-quest.com/toolbox-breakdown-getfeatureconfigdicts-function/\n",
    "\n",
    "**getPrediction**\n",
    "\n",
    "This again is fairly straight forward. We've included a few notes here, but for more detail: https://blog.quant-quest.com/toolbox-breakdown-getprediction-function/\n",
    "\n",
    "Once you've calculated the hurst exponent, this should contain the logic to use it and make profitable trades.\n",
    "\n",
    "**hurst_f**\n",
    "\n",
    "This is your time to shine! This is where you will need to implement the hurst exponent as shown in the previous lecture. There are several different ways of calculating the hurst exponent, so we recommend you use the method shown in the lecture to allow other people to easily help you - if needed!\n",
    "\n",
    "**updateCount**\n",
    "A counter"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class MyTradingFunctions():\n",
    "\n",
    "    def __init__(self):\n",
    "        self.count = 0\n",
    "        # When to start trading\n",
    "        self.start_date = '2015/01/02'\n",
    "        # When to end trading\n",
    "        self.end_date = '2017/08/31'\n",
    "        self.params = {}\n",
    "\n",
    "    def getSymbolsToTrade(self):\n",
    "        '''\n",
    "        Specify the stock names that you want to trade.\n",
    "        '''\n",
    "        return ['AAPL']\n",
    "\n",
    "    def getInstrumentFeatureConfigDicts(self):\n",
    "        '''\n",
    "        Specify all Features you want to use by creating config dictionaries.\n",
    "        Create one dictionary per feature and return them in an array.\n",
    "\n",
    "        Feature config Dictionary have the following keys:\n",
    "\n",
    "        featureId: a str for the type of feature you want to use\n",
    "        featureKey: {optional} a str for the key you will use to call this feature\n",
    "                    If not present, will just use featureId\n",
    "        params: {optional} A dictionary with which contains other optional params if needed by the feature\n",
    "\n",
    "        msDict = {\n",
    "            'featureKey': 'ms_5',\n",
    "            'featureId': 'moving_sum',\n",
    "            'params': {\n",
    "                'period': 5,\n",
    "                'featureName': 'basis'\n",
    "            }\n",
    "        }\n",
    "\n",
    "        return [msDict]\n",
    "\n",
    "        You can now use this feature by in getPRediction() calling it's featureKey, 'ms_5'\n",
    "        '''\n",
    "\n",
    "        ma1Dict = {\n",
    "            'featureKey': 'ma_90',\n",
    "            'featureId': 'moving_average',\n",
    "            'params': {\n",
    "                'period': 90,\n",
    "                'featureName': 'adjClose'\n",
    "            }\n",
    "        }\n",
    "        mom30Dict = {\n",
    "            'featureKey': 'mom_30',\n",
    "            'featureId': 'momentum',\n",
    "            'params': {\n",
    "                'period': 30,\n",
    "                'featureName': 'adjClose'\n",
    "            }\n",
    "        }\n",
    "        mom10Dict = {\n",
    "            'featureKey': 'mom_10',\n",
    "            'featureId': 'momentum',\n",
    "            'params': {\n",
    "                'period': 10,\n",
    "                'featureName': 'adjClose'\n",
    "            }\n",
    "        }\n",
    "        \n",
    "        return [ma1Dict, mom10Dict, mom30Dict]\n",
    "\n",
    "    def getPrediction(self, time, updateNum, instrumentManager, predictions):\n",
    "        '''\n",
    "        Combine all the features to create the desired predictions for each stock.\n",
    "        'predictions' is Pandas Series with stock as index and predictions as values\n",
    "        We first call the holder for all the instrument features for all stocks as\n",
    "            lookbackInstrumentFeatures = instrumentManager.getLookbackInstrumentFeatures()\n",
    "        Then call the dataframe for a feature using its feature_key as\n",
    "            ms5Data = lookbackInstrumentFeatures.getFeatureDf('ms_5')\n",
    "        This returns a dataFrame for that feature for ALL stocks for all times upto lookback time\n",
    "        Now you can call just the last data point for ALL stocks as\n",
    "            ms5 = ms5Data.iloc[-1]\n",
    "        You can call last datapoint for one stock 'ABC' as\n",
    "            value_for_abs = ms5['ABC']\n",
    "\n",
    "        Output of the prediction function is used by the toolbox to make further trading decisions and evaluate your score.\n",
    "        '''\n",
    "\n",
    "        self.updateCount() # uncomment if you want a counter\n",
    "\n",
    "        # holder for all the instrument features for all instruments\n",
    "        lookbackInstrumentFeatures = instrumentManager.getLookbackInstrumentFeatures()\n",
    "\n",
    "        #############################################################################################\n",
    "        ### TODO : FILL THIS FUNCTION TO RETURN A BUY (1), SELL (0) or LEAVE POSITION (0.5) prediction  \n",
    "        ### for each stock\n",
    "        ### USE TEMPLATE BELOW AS EXAMPLE\n",
    "        ###\n",
    "        ### HINT: Use the Hurst Exponent \n",
    "        ### http://analytics-magazine.org/the-hurst-exponent-predictability-of-time-series/\n",
    "        #############################################################################################\n",
    "        \n",
    "        # TODO: Fill in the logic for the Hurst Exponent\n",
    "        def hurst_f(input_ts, lags_to_test=20):  \n",
    "            # interpretation of return value\n",
    "            # hurst < 0.5 - input_ts is mean reverting\n",
    "            # hurst = 0.5 - input_ts is effectively random/geometric brownian motion\n",
    "            # hurst > 0.5 - input_ts is trending\n",
    "            hurst = 0.5\n",
    "            return hurst \n",
    "\n",
    "        # dataframe for a historical instrument feature (ma_90 in this case). The index is the timestamps\n",
    "        # of upto lookback data points. The columns of this dataframe are the stock symbols/instrumentIds.\n",
    "        mom30Data = lookbackInstrumentFeatures.getFeatureDf('mom_30')\n",
    "        ma90Data = lookbackInstrumentFeatures.getFeatureDf('ma_90')\n",
    "        \n",
    "        # TODO: We're trading on the 30 day momentum here and losing money, try trading on the basis of Hurst\n",
    "        # exponent and see if you're able to make money\n",
    "        if len(ma90Data.index) > 20:\n",
    "            mom30 = mom30Data.iloc[-1]\n",
    "            # Go long if momentum is positive\n",
    "            predictions[mom30 > 0] = 1\n",
    "            # Go short if momentum is negative\n",
    "            predictions[mom30 <= 0] = 0          \n",
    "        else:\n",
    "            # If no sufficient data then leave all positions\n",
    "            predictions.values[:] = 0.5\n",
    "        return predictions\n",
    "\n",
    "    def updateCount(self):\n",
    "        self.count = self.count + 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Initialize everything we've created so far"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tf = MyTradingFunctions()\n",
    "tsParams = MomentumTradingParams(tf)\n",
    "tradingSystem = TradingSystem(tsParams)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Start Trading ...\n",
    "You'll see your pnl as the backtesting runs. If you want more detailed results, two folders: `runLogs` and `tb_logs` are generated in the same directory as this script. You'll find the csv's for results inside `runLogs` and tensorboard log inside `tb_logs` "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "results = tradingSystem.startTrading()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### What next:\n",
    "After you successfully run the notebook, go back to `MyTradingFunctions` and within `getPrediction` function make following changes:\n",
    "1. Implement the logic for Hurst Exponent (Under the TODO)\n",
    "2. Write the logic to trade using Hurst Exponent (Under another TODO)\n",
    "3. Initialize and run again"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Follow-up Work\n",
    "\n",
    "It's time for those training wheels to come off. By now you've gotten yourself acquainted with using a small part of Auquan's toolbox for implementing a momentum trading strategy. But there's so much more you can do. \n",
    "\n",
    "One possible way to build on this exercise is to change the way we're predicting momentum. This could involve replacing the Hurst exponent with an arbitrarily complex machine learning model that makes forward looking predictions about whether a stock will show momentum. Then we could use those predictions to trade. If these predictions are better than everyone else using Hurst, we will have secured an edge over the market.\n",
    "\n",
    "In order to solve the problem in this manner, you need to collect a lot of data. This data is normally a closely guarded secret as it forms part of a hedge fund's informational edge. We've created a series of problems on Quant Quest to allow you to use this approach using real hedge fund data sourced from a top European institution.\n",
    "\n",
    "Some of the models that you could try:\n",
    "\n",
    "- LSTM\n",
    "- Attention Model\n",
    "- Transformers\n",
    "- etc!\n",
    "\n",
    "There are three problems that form the momentum machine learning pathway (p1,p2 and p3). Initially you will have to make a simple submission at https://quant-quest.com/problem/crwpcp1, then you will build up to the full problem. \n",
    "\n",
    "To access the complete pathway for Momentum strategies using ML, start here: https://links.quant-quest.com/momentumMachineLearningProblem\n",
    "\n",
    "**Best of Luck and Happy Learning!**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "environment": {
   "name": "tf2-2-2-gpu.2-2.m50",
   "type": "gcloud",
   "uri": "gcr.io/deeplearning-platform-release/tf2-2-2-gpu.2-2:m50"
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
